<?php
/**
 * scottlib Criteria
 * @author Aaron Collegeman aaroncollegeman.com
 * @since 0.1.3-BETA
 *
 * Copyright (C)2008-2009 Collegeman.net, LLC.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 */

///////////////////////////////////////////////////////////////////////////////
// Don't touch anything in this file.  That way when there's an update, you
// can just overwrite this file with the new one.  Go ahead, we warned you!
///////////////////////////////////////////////////////////////////////////////

/**
 * Classes capable of building queries from slCriteria objects must this class.
 * @since 0.1.3-BETA
 */
abstract class slCriteriaBuilder {
	
	function build(slCriteria $criteria);
	
	function __call($name) {
		throw new Exception("The criteria builder $name is not implemented in ".get_class($this));
	}
	
}

/**
 * @return String A String representing a global import of the scalar
 * references to slCriteria functions
 */
function criteria_fx() {
	return 'global $and, $or, $not, $eq, $ne, $each;';
}

/**
 * The criteria baseclass.
 * @since 1.0.3-BETA
 */
class slCriteria {
	
	private $criterion;
	
	protected $values;
	
	function __construct($args) {
		$this->criterion = $this->getCriteronFromArgs(func_get_args());
	}

	protected final function getCriterionFromArgs($args) {
		if (count($args) == 1 && is_array($args[0]))
			return $args[0];
		else
			return $args;
	}
	
	public final function setValues($values) {
		$this->values = $values;
	}
	
	public final function getValues() {
		return $this->values;
	}
	
	public final function getCriterion() {
		return $this->criterion;
	}
	
}

function criteria_() {
	return new slCriteria(func_get_args());
}

$criteria = "criteria";

class slAndCriteria extends Criteria {}

function and_() {
	return new slAndCriteria(func_get_args());
}
$and = "and_";

class slOrCriteria extends slCriteria {
	
	private $criterion;
	
	function __construct($args) {
		$this->criterion = $this->getArgs($args);
	}
	
	function build(slObject $obj) {
		$sql = array();
		foreach($this->criterion as $c) {
			$c->setValues($this->values);
			$this->push($obj, $sql, $c->build($obj));
		}
		return " ( " . join(" OR ", $sql) . " ) ";
	}
	
}

function or_() {
	return new slOrCriteria(func_get_args());
}
$or = "or_";

class slNotCriteria extends slCriteria {
	
	private $criterion;
	
	function __construct($args) {
		$this->criterion = $this->getArgs($args);
	}
	
	function build(slObject $obj) {
		$sql = array();
		foreach($this->criterion as $c) {
			$c->setValues($this->values);
			$result = $c->build($obj);
			$sql[] = (is_object($result) && in_array("slCriteria", class_parents($result)) ? $result->build($obj) : $result);
		}
		return " NOT ( " . join(" ", $sql) . " ) ";
	}
	
}

function not_() {
	return new slNotCriteria(func_get_args());
}
$not = "not_";

class slEqCriteria extends slCriteria {
	
	private $propertyName;
	
	private $value;
	
	function __construct($propertyName, $value) {
		$this->propertyName = $propertyName;
		$this->value = $value;
	}
	
	function build(slObject $obj) {
		
		// TODO: get the field name
		$fieldName = $this->propertyName;
		
		if (count($this->values)) {
			$sql = $obj->getConn()->prepare("`$fieldName` = $this->value", $this->values);
		}
		else {
			$sql = $obj->getConn()->prepare("# = ?", array($fieldName, $this->value));
		} 

		return $sql;
		
	}
	
} 

function eq_($propertyName, $value) {
	return new slEqCriteria($propertyName, $value);	
}
$eq = "eq_";

class slNeCriteria extends slCriteria {
	
	private $propertyName;
	
	private $value;
	
	function __construct($propertyName, $value) {
		$this->propertyName = $propertyName;
		$this->value = $value;
	}
	
	function build(slObject $obj) {
		
		// get the field name
		$fieldName = $this->propertyName;
		
		if (count($this->values)) {
			$sql = $obj->getConn()->prepare("`$fieldName` <> $this->value", $this->values);
		}
		else {
			$sql = $obj->getConn()->prepare("# <> ?", array($fieldName, $this->value));
		} 

		return $sql;
		
	}
	
} 

function ne_($propertyName, $value) {
	return new slNeCriteria($propertyName, $value);	
}
$ne = "ne_";

class slEachCriteria extends slCriteria {
	
	private $in;
	
	private $names;
	
	private $criteria;
	
	function __construct($in, $names, $criteria) {
		$this->in = $in;
		$this->names = is_array($names) ? $names : array($names);
		$this->criteria = $criteria;	
	}
	
	function build(slObject $obj) {
		if (!$this->values)
			$this->values = array();
			
		$criterion = array();
		
		foreach($this->in as $data) {
			if (is_object($data) || is_array($data)) {
				foreach($this->names as $name) {
					if (is_object($data))
						$this->values[$name] = $data->$name;
					else
						$this->values[$name] = $data[$name];
				}
			}
			else {
				foreach($this->names as $name)
					$this->values[$name] = $data;
			}
			
			$c = clone $this->criteria;
			$c->setValues($this->values);
			$criterion[] = $c;
		}
		
		return $criterion;
	}
}

function each_($in, $names, $criteria) {
	return new slEachCriteria($in, $names, $criteria);
}
$each = "each_";

class slIfCriteria extends slCriteria {
	
	private $boolean;
	
	private $criterion;
	
	function __construct($boolean, $criterion) {
		$this->boolean = $boolean;
		$this->criterion = $criterion;
	}
	
	function build(slObject $obj, $values = null) {
		
	}
	
}

function if_($boolean, $criterion) {
	return new slIfCriteria($boolean, $criterion);
}
$if = "if_";






